package big

import (
	"fmt"
	"math"
	"strconv"
)

/*
	分数输出

原理：

	分子与分母间用/隔开，可表示分数
*/
func (r Rat) 输出() { //输出分数
	fmt.Println(strconv.Itoa(r.分子) + "/" + strconv.Itoa(r.分母))
}

/*
	分数倒数

原理：

	分子分母交换位置
*/
func (分数值 分数) 倒数() 分数 { //求一个分数的倒数
	var (
		倒数 分数
	) // 使返回值为参数倒数
	倒数.分母 = 分数值.分子
	倒数.分子 = 分数值.分母
	return 倒数
}

/*
	构造分数

原理：

	分子分母分别赋值
*/
func (分数值 *分数) 构造(分子, 分母 int) { //构造一个分数
	(*分数值).分子 = 分子
	(*分数值).分母 = 分母
}

/*
	分数比较

原理：

	先通分，再比较分子，分子大的为大
*/
func (分数值 分数) 比较(相比值 分数) int8 { //比较一分数与另一分数的大小
	正确分母 := 求最小公倍数(分数值.分母, 相比值.分母)
	分子修正系数 := [2]int{正确分母 / 分数值.分母, 正确分母 / 相比值.分母}
	分数值.分子 = 分数值.分子 * 分子修正系数[0] //通分
	相比值.分子 = 相比值.分子 * 分子修正系数[1] //通分
	if 分数值.分子 > 相比值.分子 {
		return 1
	} else if 分数值.分子 < 相比值.分子 {
		return -1
	} else {
		return 0
	}
}

/*
	求最小公倍数

原理：

	存在倍数关系大数为最小公倍数
	非素数必为2或3或5倍数，重复（用1与上述三数中的被求数公因数相乘，并除之公因数），无公因数与剩余被求数相乘，所得位最小公倍数
	素数相乘即为最小公倍数
*/
func 求最小公倍数(分母1, 分母2 int) int { //求两个数的最小公倍数
	var (
		最小公倍数 int = 1
	)
	if 分母1%分母2 == 0 { //如果分母1是最小公倍数
		return 分母1
	} else if 分母2%分母1 == 0 { //如果分母2是最小公倍数
		return 分母2
	} else { //如果分母1或分母2不是最小公倍数
		for {
			if 分母1%2 == 0 && 分母2%2 == 0 {
				最小公倍数 *= 2
				分母1 /= 2
				分母2 /= 2
			} else if 分母1%5 == 0 && 分母2%5 == 0 {
				最小公倍数 *= 5
				分母1 /= 5
				分母2 /= 5
			} else if 分母1%3 == 0 && 分母2%3 == 0 {
				最小公倍数 *= 3
				分母1 /= 3
				分母2 /= 3
			} else {
				最小公倍数 *= 分母1
				最小公倍数 *= 分母2
				break
			}
		}
		return 最小公倍数
	}
}

/*
	整数转分数

原理：

	分母为1，分子为整数值
*/
func 整数转分数(整数 int) 分数 { //把整数转为分数
	var (
		分数 分数
	)
	分数.分子 = 整数
	分数.分母 = 1
	return 分数
}

/*
	分数相加

原理：

	分母相同，分子相加
	分母不同，通分后分子相加
	分母为两加数分母，最小公倍数
*/
func 分数相加(加数1, 加数2 分数) 分数 { //把两个分数相加
	var 和 分数
	if 加数1.分母 == 加数2.分母 { //分母相同
		和.分母 = 加数1.分母
		和.分子 = 加数1.分子 + 加数2.分子
	} else { //分母不同
		正确分母 := 求最小公倍数(加数1.分母, 加数2.分母)
		加数1.分子 *= 正确分母 / 加数1.分母
		加数2.分子 *= 正确分母 / 加数2.分母
		和.分子 = 加数1.分子 + 加数2.分子
		和.分母 = 正确分母
	}
	return 和
}

/*
	分数相减

原理：

	分母相同，分子相减
	分母不同，通分后分子相减
	分母为两减数分母，最小公倍数
*/
func 分数相减(被减数, 减数 分数) 分数 { //把两个分数相减
	var 差 分数
	if 被减数.分母 == 减数.分母 { //分母相同
		差.分母 = 被减数.分母
		差.分子 = 被减数.分子 - 减数.分子
	} else { //分母不同
		正确分母 := 求最小公倍数(被减数.分母, 减数.分母)
		被减数.分子 *= 正确分母 / 被减数.分母
		减数.分子 *= 正确分母 / 减数.分母
		差.分子 = 被减数.分子 - 减数.分子
		差.分母 = 正确分母
	}
	return 差
}

/*
	分数相乘

原理：

	分子乘分子，分母乘分母
*/
func 分数相乘(乘数1, 乘数2 分数) 分数 { //把两个分数相乘
	var (
		积 分数
	) // 分子乘分子，分母乘分母
	积.分子 = 乘数1.分子 * 乘数2.分子
	积.分母 = 乘数1.分母 * 乘数2.分母
	return 积
}

/*
	分数相除

原理：

	除法可转换为，被除数乘除数倒数
*/
func 分数相除(被除数, 除数 分数) 分数 { //把两个分数相乘除
	var (
		商 分数
	) // 被除数乘除数倒数
	除数 = 除数.倒数()
	商.分母 = 被除数.分母 * 除数.分母
	商.分子 = 被除数.分子 * 除数.分子
	return 商
}

/*
	分数整数幂

原理:

	分数整数幂等于分子分母分别与指数进行幂运算
*/
func 分数整数幂(底数 分数, 指数 int) 分数 { //求分数整数幂
	var (
		幂 分数
	) //分子分母分别与指数进行幂运算
	幂.分子 = int(math.Pow(float64(底数.分子), float64(指数)))
	幂.分母 = int(math.Pow(float64(底数.分母), float64(指数)))
	return 幂
}
